#include "CdiD3DWindow.h"

// D3D

CdiD3DWindow::CdiD3DWindow(WNDPROC proc) : 
CdiWindow( proc, _T("D3D Window"), 800, 600 ), 
m_bFullScreen( false ), 
m_pDevice( NULL ),
m_pD3D( NULL )
{
  InitD3D();  
}

CdiD3DWindow::CdiD3DWindow(WNDPROC proc, bool fullScreen, int width, int height ) :
CdiWindow( proc, _T("D3D Window"), width, height ), 
m_bFullScreen( fullScreen ), 
m_pDevice( NULL ),
m_pD3D( NULL )
{
  InitD3D();
}

CdiD3DWindow::~CdiD3DWindow(void)
{
  if( m_pDevice )
  {
    m_pDevice->Release();
    m_pDevice = NULL;
  }
  if( m_pD3D )
  {
    m_pD3D->Release();
    m_pD3D = NULL;
  }
}

void CdiD3DWindow::InitD3D()
{
  CdiWindow::AssertValid();
  m_pD3D = Direct3DCreate9( D3D_SDK_VERSION );
  _ASSERT( m_pD3D != NULL );

  D3DPRESENT_PARAMETERS d3dParams;
  HRESULT hr;

  InitD3DParameters( d3dParams );  
  hr = CreateDevice( d3dParams );
  AssertValid();
  
  if( FAILED( hr ) ) 
  {
    return;
  }

  CreateViewPort();
  CreateProjectionMatrice();

  // get the counter per second
  QueryPerformanceFrequency( (LARGE_INTEGER*)&m_frequency );
}

int CdiD3DWindow::CalculateFrameRate()
{
  INT64 newCount = 0;
  static INT64 lastCount = 0;
  static INT64 frameCount = -1;
  INT64 difference = 0;

  QueryPerformanceCounter( (LARGE_INTEGER*)&newCount );

  if( newCount == 0 )
  {
    // unable to count frames
    return -1;
  }

  frameCount++;

  difference = newCount - lastCount;

  if( difference >= m_frequency )
  {
    m_frameRate = (int)frameCount;
    frameCount = 0;
    lastCount = newCount;
  }
  return m_frameRate;
}

void CdiD3DWindow::InitD3DParameters(D3DPRESENT_PARAMETERS& params)
{
  ZeroMemory( &params, sizeof( D3DPRESENT_PARAMETERS ) );

  params.AutoDepthStencilFormat = D3DFMT_D16;
  params.EnableAutoDepthStencil = TRUE;
  params.SwapEffect = D3DSWAPEFFECT_DISCARD;
  params.Windowed = !m_bFullScreen;
  params.BackBufferFormat = D3DFMT_UNKNOWN;
  params.hDeviceWindow = m_hWnd;
  params.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;

  // Init full screen
  if( TRUE == m_bFullScreen ) 
  {
    params.BackBufferCount = 1;
    params.BackBufferHeight = m_screenHeight;
    params.BackBufferWidth = m_screenWidth;
    params.BackBufferFormat = D3DFMT_A8R8G8B8;
  }
}

// Creates the device context
HRESULT CdiD3DWindow::CreateDevice( D3DPRESENT_PARAMETERS& d3dParams  )
{
  // Create the Device
  HRESULT hr;
  hr = m_pD3D->CreateDevice( D3DADAPTER_DEFAULT, 
    D3DDEVTYPE_HAL, 
    m_hWnd, 
    D3DCREATE_SOFTWARE_VERTEXPROCESSING, 
    &d3dParams, 
    &m_pDevice );

  AssertValid();

  if( FAILED( hr ) ) 
  {
    return E_FAIL;
  }

  m_pDevice->AddRef();
  return S_OK;
}

void CdiD3DWindow::Start()
{
  if( false == IsInit() )
  {
    return;
  }

  AssertValid();

  UpdateWindow( m_hWnd );
  ShowWindow( m_hWnd, SW_SHOW );

  BOOL bDone = FALSE;
  MSG msg;
  ZeroMemory( &msg, sizeof( MSG ) );

  while( FALSE == bDone )
  {
    if( PeekMessage(&msg, NULL, NULL, NULL, PM_REMOVE ))
    {
      if( msg.message == WM_QUIT ) 
      {
        bDone = TRUE;
      }
      else 
      {
        TranslateMessage( &msg );
        DispatchMessage( &msg );
      }
    }
    else 
    {
      GameLoop();
    }
  }
}

void CdiD3DWindow::CreateViewPort()
{
  if( m_pDevice != NULL )
  {
    D3DVIEWPORT9 viewPort;
    viewPort.X = 0;
    viewPort.Y = 0;
    viewPort.Width = this->m_screenWidth;
    viewPort.Height = this->m_screenHeight;
    viewPort.MinZ = 0.0f;
    viewPort.MaxZ = 1.0f;

    m_pDevice->SetViewport( &viewPort );
  }
}

void CdiD3DWindow::CreateProjectionMatrice()
{
  if( m_pDevice != NULL )
  {
    D3DXMATRIX projectionMatrix;
    ZeroMemory(&projectionMatrix, sizeof( D3DXMATRIX ) );
    float screenAspect = (float)m_screenWidth / (float)m_screenHeight;
    float fov = D3DX_PI / 4.0f;

    D3DXMatrixPerspectiveFovLH( &projectionMatrix, fov, screenAspect, 1.0f, 1000.0f );

    HRESULT hr = m_pDevice->SetTransform( D3DTS_PROJECTION, &projectionMatrix );
    if( FAILED( hr ) )
    {
      OutputDebugString( _T("*** Unable to SET perspective matrix\n"));
      throw -1;
    }
  }
}

void CdiD3DWindow::SetViewMatrix( D3DXVECTOR3& position, 
                                 D3DXVECTOR3& lookAt, 
                                 D3DXVECTOR3& up )
{
  if( m_pDevice != NULL )
  {
    D3DXMATRIX viewMatrix;
    D3DXMatrixLookAtLH( &viewMatrix, 
      &position,
      &lookAt,
      &up
      );

    HRESULT hr = m_pDevice->SetTransform( D3DTS_VIEW, &viewMatrix );
    if( FAILED( hr ) ) 
    {
      OutputDebugString(_T("Unable to set view Matrix"));
      throw -1;
    }
  }
}

void CdiD3DWindow::AssertValid()
{
  CdiWindow::AssertValid();
  _ASSERT( m_pDevice != NULL );
}
// Enable or disable the lighting
void CdiD3DWindow::SetLightingState(BOOL Enabled)
{
  AssertValid();
  this->m_pDevice->SetRenderState( D3DRS_LIGHTING, Enabled );
}

void CdiD3DWindow::ResetScreen()
{

}
